package Uebung03;

import java.io.IOException;

/**
 * Implementierung des Nimm-Spiels zwischen Wumpus (Zufallsgenerierte Züge)
 * und Mensch (Realisierung durch minimax-Algorithmus)
 * 
 * Es gewinnt der Spieler der nicht den letzten Knochen zieht
 * 
 * @author Robert Thiel, Janka W�bbekind, Jonas Hansen, Nils Kunsem�ller
 *
 */

public class Wumpus2{
	
	/**
	 * Rechnet den neuen state aus abhängig vom aktuellen
	 * Zustand und der Aktion
	 * 
	 * @param action Anzahl der zu ziehenden Knochen
	 * @param state aktueller Zustand
	 * @return neuer Zustand
	 */

	public static int getResult(int action, int state) {
		
		return state-action;
		
	}
	
	/**
	 * Überprüft ob der übergebene Zustand ein
	 * Terminierungszustand ist  
	 * 
	 * @param state zu überprüfender Zustand
	 * @return Wenn Terminierungszustand true, sonst false
	 */
	
	public static boolean isTerminated(int state) {
		if (state<=5) return true;
		else return false;
	}
	
	/**
	 * Berechnet die Bewertung des aktuellen Zustands und gibt diese zurück.
	 * Die Bewertung hängt von der aufrufenden Funktion ab.
	 * Wenn maxValue aufruft und der Zustand ein über dem Vielfachen von 4 ist,
	 * wird 1 zurückgegeben
	 * Wenn minValue aufruft und der Zustand ein über dem Vielfachen von 4 ist,
	 * wird -1 zurückgegeben
	 * Sonst wird 0 zurückgegeben
	 * 
	 * @param state aktueller Zustand
	 * @param max true wenn maxValue aufruft, wenn minValue aufruft false
	 * @return -1,0,1 siehe oben
	 */
	
	public static int getUtility(int state, boolean max) {
		int m = state%4+1;
		if (max && m==0) return 1;
		else if (!max && m==0) return -1;
		else return 0;
	}
	
	/**
	 * Rekursive Berechnung des maxValues.
	 * Abbruchkriterium ist isTerminated()
	 * value wird auf kleinsten Integerwert initialisiert
	 * In der for-Schleife werden die 3 möglichen Aktionen für
	 * den aktuellen Zustand durchgegangen und minValue wird rekursiv
	 * aufgerufen 
	 * 
	 * @param state aktueller Zustand
	 * @return der maximale Wert der tieferliegenden minimalen Werte
	 */
	
	public static int maxValue(int state) {
		if (isTerminated(state)) return getUtility(state, true);
		else {
			int value = Integer.MIN_VALUE;
			
			for(int action=1; action<=3; action++) {
				value = Math.max(value, minValue(getResult(state, action)));
			}
			return value;
		}
	}
	
	/**
	 * Rekursive Berechnung des minValues.
	 * Abbruchkriterium ist isTerminated()
	 * value wird auf größten Integerwert initialisiert
	 * In der for-Schleife werden die 3 möglichen Aktionen für
	 * den aktuellen Zustand durchgegangen und maxValue wird rekursiv
	 * aufgerufen 
	 * 
	 * @param state aktueller Zustand
	 * @return der minimale Wert der tieferliegenden maximalen Werte
	 */
	
	public static int minValue(int state) {
		if (isTerminated(state)) return getUtility(state, false);
		else {
			int value = Integer.MAX_VALUE;
			
			for(int action=1; action<=3; action++) {
				value = Math.min(value, maxValue(getResult(state, action)));
			}
			return value;
		}
	}
	
	/**
	 * Einstiegsfunktion zur rekursiven Berechnung des MiniMax-Wertes.
	 * Vom Initalzustand werden in einer for-Schleife die drei Aktionen
	 * durchgeführt und maxValue() mit dem entsprechenden neuen Zustand
	 * aufgerufen.
	 * result wird mit 1 initialisiert, da bei nicht vorhandenem optimalen
	 * Zug 1 Knochen gezogen werden soll.
	 * Beim Fund eines optimalen Zuges wird result auf die entsprechende Aktion gesetzt.
	 * resultValue wird auf den neuen optimalen Zug gesetzt
	 * 
	 * @param state Initialzustand
	 * @return Anzahl der abzuziehenden Knochen 
	 */
	
	public static int minimax(int state) {
		
		int result = 1;
		int resultValue = 0;
		for(int action=1; action<=3; action++) {
			int value = maxValue(getResult(state, action));
			if (value > resultValue) {
				result = action;
				resultValue = value;
			}
		}
		return result;
	}
	
	/**
	 * Spielzug vom Menschen mit MiniMax Algorithmus
	 * Wenn als Zustand 0 übergeben wird hat M bereits gewonnen
	 * Nach dem Zug ist Wumpus mit Spielzug dran 
	 * 
	 * @param state Aktuelle Anzahl an verbleibenden Knochen
	 */

	public static void mensch(int state){
		if(state==0){
			System.out.println();
			System.out.println("Mensch gewinnt");
			return ;
		}
		int knochen = minimax(state);
		System.out.println("Mensch nimmt " + knochen + " Knochen");
		int newState = state - knochen;
		System.out.println(newState + " Knochen verbleiben");
		wumpus(newState);
	}
	

	
	/**
	 * Spielzug vom Wumpus mit Zufallsgenerator
	 * Wenn als Zustand 0 übergeben wird hat Wumpus bereits gewonnen
	 * Wenn der Wumpus 1 Knochen bekommt, nimmt er diesen
	 * Wenn der Wumpus 2 Knochen bekommt, nimmt er 1 oder 2
	 * Sonst zieht er 1, 2 oder 3 Knochen
	 * Nach dem Zug ist Mensch mit Spielzug dran 
	 * 
	 * @param state Aktuelle Anzahl an verbleibenden Knochen
	 */

	public static void wumpus(int state){
		if(state==0){
			System.out.println();
			System.out.println("Wumpus gewinnt");
			return ;
		}
		int knochen = 0;
		if(state == 1) knochen = 1;
		else if(state == 2) knochen = (int) (Math.random()*2+1);
		else knochen = (int) (Math.random()*3+1);
		System.out.println("Wumpus nimmt " + knochen + " Knochen");
		int newState = state - knochen;
		System.out.println(newState + " Knochen verbleiben");
		System.out.println();
		mensch(newState);
	}
	
	/**
	 * initale Anzahl an Knochen wird übergeben
	 * Mensch beginnt mit Spielzug
	 * 
	 * @param args
	 * @throws IOException
	 */
	
	public static void main(String[] args) throws IOException{
		int state = Integer.valueOf(args[0]).intValue();
		System.out.println("Initialzustand: " + state + " Knochen");
		System.out.println();
		mensch(state);
	}
}